package org.thinginitself.mixpath.dom;

import org.jaxen.NamedAccessNavigator;
import org.jaxen.UnsupportedAxisException;
import org.jaxen.dom.DocumentNavigator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author Andrew Harrison
 * @version 1.0.0 15/05/2011
 */
public class DomMiXPathNavigator extends DocumentNavigator implements NamedAccessNavigator {

    private static DomMiXPathNavigator INSTANCE = new DomMiXPathNavigator();

    public static DomMiXPathNavigator getNavigator() {
        return INSTANCE;
    }


    public Iterator getChildAxisIterator(Object contextNode,
                                         String localName,
                                         String namespacePrefix,
                                         String namespaceURI)
            throws UnsupportedAxisException {
        boolean ns = namespaceURI != null;
        Node parent = (Node) contextNode;
        List<Node> ret = new ArrayList<Node>();
        if (parent.hasChildNodes()) {
            NodeList nodes = parent.getChildNodes();
            for (int i = 0; i < nodes.getLength(); i++) {
                Node n = nodes.item(i);
                if (n instanceof Element) {
                    // NOTE: Local name could be null here
                    if (ns) {
                        if (((Element) n).getTagName().equals(localName) && namespaceURI.equals(n.getNamespaceURI())) {
                            ret.add(n);
                        }
                    } else {
                        String local = ((Element) n).getTagName();
                        if (local == null) {
                            local = n.getLocalName();
                        }
                        if ((localName != null && localName.equals(local)) && (n.getNamespaceURI() == null || n.getNamespaceURI().length() == 0)) {
                            ret.add(n);
                        }
                    }

                }
            }
        }
        if (ret.size() == 0) {
            boolean add = true;
            if ((parent.getNodeType() == Node.DOCUMENT_NODE)) {
                Document doc = (Document) parent;
                if (doc.getDocumentElement() != null) {
                    add = false;
                }
            }
            if (add) {
                Element e;
                if (ns) {
                    e = DomHelp.getOwnerDocument(parent).createElementNS(namespaceURI, localName);
                } else {
                    e = DomHelp.getOwnerDocument(parent).createElement(localName);
                }

                parent.appendChild(e);
                if (ns) {
                    String pref = DomHelp.getPrefix(e, namespaceURI);
                    if (pref != null) {
                        e.setPrefix(pref);
                    }
                }
                ret.add(e);
            }
        }
        return new NodeIterator(ret);
    }

    public Iterator getAttributeAxisIterator(Object contextNode,
                                             String localName,
                                             String namespacePrefix,
                                             String namespaceURI)
            throws UnsupportedAxisException {
        return null;
    }


    private static class NodeIterator implements Iterator {

        private List<Node> nodes;
        private int count = 0;
        private int len;

        private NodeIterator(List<Node> nodes) {
            this.nodes = nodes;
            len = nodes.size();
        }

        public boolean hasNext() {
            return count < len;
        }

        public Object next() {
            if (count >= len) {
                return null;
            }
            Node n = nodes.get(count);
            count++;
            return n;
        }

        public void remove() {
            throw new UnsupportedOperationException("Remove is not supported");
        }
    }

}
